﻿#define  _CRT_SECURE_NO_WARNINGS
#include "Common.h"
#include "ThreadCache.h"
#include "CentralCache.h"



//申请空间
void* ThreadCache::Allocate(size_t size)
{
	
	assert(size <= MAX_BYTES);//申请空间的大小不能超过256Kb
	size_t alignSize = SizeClass::RoundUp(size);//确定其对齐数

	//申请空间的大小对应在哪个桶里呢？
	size_t index = SizeClass::Index(size);

	//向桶里申请
	if (!_freeLists[index].Empty())  //_freeLists[index]//获取数组中某个对象  
	{
		return _freeLists[index].Pop();//如果不为空，就弹一个节点地址给线程用
	}
	//向中心缓存申请
	else
	{
		return FetchFromCentralCache(index, alignSize);//如果没有空间，则向中心申请空间
	}
}

//central cache也是一个哈希桶结构，思路和thread_cache类似，但是central cache是所有线程共享的，所以需要加锁！
//但是这里的锁是桶锁，不是给整个中心缓存加锁，假设线程1和线程2申请的是不同桶，就不会加锁，只有当线程1和线程2申请同一个桶时，才会加锁。因此叫桶锁
void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)//size是对齐数
{ 
	//慢开始反馈调节算法
	//1.最开始不会向 central cache 一次批量要太多，因为要太多会用不完
	//2.如果不断有size大小的内存需求，那么batchNum 就会不断增长，直到上限，2~512
	//3.size越大，一次向central cache要的batchNum越小
	//4.size越小，一次向central cache要的越大(慢慢增大)
	size_t batchNum = min(_freeLists[index].MaxSize(), SizeClass::NumMoveSize(size));//当申请空间的字节较小时，batchNum = 1；//thread cache 向central cache 申请对应span下节点的个数
	if (_freeLists[index].MaxSize() == batchNum)
	{
		_freeLists[index].MaxSize() += 1;//Max
	}

	void* start = nullptr;
	void* end = nullptr;
	//实际申请到的span结构下的链表个数
	size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start,end,batchNum,size);//向central cache申请空间
	assert(actualNum > 0);

	if (actualNum == 1)//central cache第一次传递空间给thread cache时，只会开辟一个，将该地址传递到thread cache的桶中链
	{
		assert(start == end);
		return start;
	}
	else//多次申请空间时，此时为多节点头插近桶中， 
	{
		_freeLists[index].PushRange(NextObj(start),end,actualNum-1);
		return start;
	}
}

void* ThreadCache::DeAllocate(void* ptr, size_t size) //size 申请空间的大小
{
	assert(ptr);
	assert(size <= MAX_BYTES);

	size_t index = SizeClass::Index(size);
	_freeLists[index].Push(ptr);//向thread cache index对应桶归还一个空间节点

	//当链表长度大于一次要批量申请的内存时，开始还一段list给central cache
	if (_freeLists[index].Size() >= _freeLists[index].MaxSize())
	{
		ListTooLong(_freeLists[index], size);
	}

	return nullptr;
}


void ThreadCache::ListTooLong(FreeList& list, size_t size)
{
	void* start = nullptr;
	void* end = nullptr;
	list.PopRange(start, end, list.MaxSize());

	CentralCache::GetInstance()->ReleaseListToSpans(start, size);
}